package com.gzc.just.play.last.war.common.share.performance;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;

/**
 * Virtual thread manager
 * Manages virtual threads and task execution
 */
@Component
public class VirtualThreadManager {
    
    private static final Logger log = LoggerFactory.getLogger(VirtualThreadManager.class);
    
    private final PerformanceMonitor performanceMonitor;
    
    // Virtual thread executor
    private final ExecutorService virtualThreadExecutor = Executors.newVirtualThreadPerTaskExecutor();
    
    // Statistics
    private final AtomicLong createdThreads = new AtomicLong(0);
    private final AtomicLong activeThreads = new AtomicLong(0);
    private final AtomicLong completedTasks = new AtomicLong(0);
    
    public VirtualThreadManager(PerformanceMonitor performanceMonitor) {
        this.performanceMonitor = performanceMonitor;
    }
    
    /**
     * Execute task asynchronously
     */
    public CompletableFuture<Void> runAsync(Runnable task) {
        return CompletableFuture.runAsync(wrapTask(task, false), virtualThreadExecutor);
    }
    
    /**
     * Execute task asynchronously with return value
     */
    public <T> CompletableFuture<T> supplyAsync(Supplier<T> task) {
        return CompletableFuture.supplyAsync(wrapTask(task, false), virtualThreadExecutor);
    }
    
    /**
     * Retry task
     */
    public <T> CompletableFuture<T> retry(Supplier<T> task, int maxRetries) {
        return retry(task, maxRetries, 1000);
    }
    
    /**
     * Retry task with delay
     */
    public <T> CompletableFuture<T> retry(Supplier<T> task, int maxRetries, long delayMs) {
        CompletableFuture<T> future = new CompletableFuture<>();
        
        retryTask(task, future, 0, maxRetries, delayMs);
        
        return future;
    }
    
    /**
     * Internal retry task method
     */
    private <T> void retryTask(Supplier<T> task, CompletableFuture<T> future, 
                               int attempt, int maxRetries, long delayMs) {
        if (attempt >= maxRetries) {
            future.completeExceptionally(new RuntimeException("Max retries exceeded"));
            return;
        }
        
        CompletableFuture.supplyAsync(wrapTask(task, true), virtualThreadExecutor)
            .whenComplete((result, ex) -> {
                if (ex == null) {
                    future.complete(result);
                } else {
                    // Retry with delay
                    CompletableFuture.delayedExecutor(delayMs, TimeUnit.MILLISECONDS)
                        .execute(() -> retryTask(task, future, attempt + 1, maxRetries, delayMs));
                }
            });
    }
    
    /**
     * Execute task with timeout
     */
    public <T> CompletableFuture<T> supplyAsyncWithTimeout(Supplier<T> task, long timeout, TimeUnit unit) {
        CompletableFuture<T> taskFuture = CompletableFuture.supplyAsync(wrapTask(task, false), virtualThreadExecutor);
        CompletableFuture<T> timeoutFuture = CompletableFuture.supplyAsync(() -> {
            throw new CompletionException(new TimeoutException("Task timed out after " + timeout + " " + unit));
        }, CompletableFuture.delayedExecutor(timeout, unit));

        return taskFuture.applyToEither(timeoutFuture, result -> result)
                .whenComplete((result, ex) -> {
                    if (ex != null) {
                        taskFuture.cancel(true);
                    }
                });
    }
    
    /**
     * Wrap task to add monitoring
     */
    private Runnable wrapTask(Runnable task, boolean isRetry) {
        createdThreads.incrementAndGet();
        activeThreads.incrementAndGet();
        
        return () -> {
            try {
                task.run();
                completedTasks.incrementAndGet();
            } finally {
                activeThreads.decrementAndGet();
            }
        };
    }
    
    /**
     * Wrap task to add monitoring
     */
    private <T> Supplier<T> wrapTask(Supplier<T> task, boolean isRetry) {
        createdThreads.incrementAndGet();
        activeThreads.incrementAndGet();
        
        return () -> {
            try {
                T result = task.get();
                completedTasks.incrementAndGet();
                return result;
            } finally {
                activeThreads.decrementAndGet();
            }
        };
    }
    
    /**
     * Get statistics
     */
    public ThreadStats getStats() {
        ThreadStats stats = new ThreadStats();
        stats.setCreatedThreads(createdThreads.get());
        stats.setActiveThreads(activeThreads.get());
        stats.setCompletedTasks(completedTasks.get());
        return stats;
    }
    
    /**
     * Shutdown
     */
    public void shutdown() {
        virtualThreadExecutor.shutdown();
        try {
            if (!virtualThreadExecutor.awaitTermination(10, TimeUnit.SECONDS)) {
                virtualThreadExecutor.shutdownNow();
            }
        } catch (InterruptedException e) {
            virtualThreadExecutor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
    
    /**
     * Thread statistics class
     */
    public static class ThreadStats {
        private long createdThreads;
        private long activeThreads;
        private long completedTasks;
        
        public long getCreatedThreads() {
            return createdThreads;
        }
        
        public void setCreatedThreads(long createdThreads) {
            this.createdThreads = createdThreads;
        }
        
        public long getActiveThreads() {
            return activeThreads;
        }
        
        public void setActiveThreads(long activeThreads) {
            this.activeThreads = activeThreads;
        }
        
        public long getCompletedTasks() {
            return completedTasks;
        }
        
        public void setCompletedTasks(long completedTasks) {
            this.completedTasks = completedTasks;
        }
    }
}